#include "helper.h"

#include <cmath>

/****************************************************//**
 * Project-Namespace
 ********************************************************/
namespace jae
{

/****************************************************//**
 * This function corrects the rotationangle of the 3D
 * objects.
 * @param aAngle Angle in degree 
 ********************************************************/
void normalizeAngle(GLfloat *aAngle)
{
    while (*aAngle < 0)
      *aAngle += 360.0f;
    while (*aAngle > 360.0f)
      *aAngle -= 360.0f;
}

/****************************************************//**
 * Creates a Vector3D with given Values
 * @param aX X-Value
 * @param aY Y-Value
 * @param aZ Z-Value
 ********************************************************/
Vector3D vectorCreate3D(GLfloat aX, GLfloat aY, GLfloat aZ)
{
    Vector3D tmp;
    tmp.x = aX;
    tmp.y = aY;
    tmp.z = aZ;
    return tmp;
}

/****************************************************//**
 * Return the length of a vector.
 ********************************************************/
GLfloat vectorGetLength(Vector3D* aVector)
{
    if (aVector->x == 0.0f && aVector->y == 0.0f && aVector->z == 0.0f)
        return 0.0f;
    else
        return (GLfloat)(sqrt(SQR(aVector->x)+SQR(aVector->y)+SQR(aVector->z)));
}

/****************************************************//**
 * Return a normalized vector. (Length = 1.0f)
 ********************************************************/
Vector3D vectorNormalize(Vector3D aVector)
{
    Vector3D result;
    float length = vectorGetLength(&aVector);
    
    if (length == 0.0f) 
        return vectorCreate3D(0.0f,0.0f,0.0f);
    
    result.x = aVector.x / length;
    result.y = aVector.y / length;
    result.z = aVector.z / length;
    return result;
}

/****************************************************//**
 * Addition of 2 vectors.
 ********************************************************/
Vector3D operator+ (Vector3D aVectorA, Vector3D aVectorB)
{
    Vector3D result;
    result.x = aVectorA.x + aVectorB.x;
    result.y = aVectorA.y + aVectorB.y;
    result.z = aVectorA.z + aVectorB.z;
    return result;
}

/****************************************************//**
 * Subtraction of 2 vectors.
 ********************************************************/
Vector3D operator- (Vector3D aVectorA, Vector3D aVectorB)
{
    Vector3D result;
    result.x = aVectorA.x - aVectorB.x;
    result.y = aVectorA.y - aVectorB.y;
    result.z = aVectorA.z - aVectorB.z;
    return result;
}

/****************************************************//**
 * Multiplication of a vector and a float value.
 ********************************************************/
Vector3D operator* (Vector3D aVector, GLfloat aValue)
{
    Vector3D result;
    result.x = aVector.x * aValue;
    result.y = aVector.y * aValue;
    result.z = aVector.z * aValue;
    return result;
}

/****************************************************//**
 * Multiplication of a vector and a float value. (Dot Product)
 ********************************************************/
GLfloat operator* (Vector3D aVectorA, Vector3D aVectorB)
{
    return   aVectorA.x * aVectorB.x
           + aVectorA.y * aVectorB.y
           + aVectorA.z * aVectorB.z;
}

/****************************************************//**
 * Multiplication of a vector and a float value. (Dot Product)
 ********************************************************/
bool operator== (Vector3D aVectorA, Vector3D aVectorB)
{
    return (aVectorA.x == aVectorB.x) &&
           (aVectorA.y == aVectorB.y) &&
           (aVectorA.z == aVectorB.z);
}

/****************************************************//**
 * Multiplication of a vector and a float value. (Cross Product)
 ********************************************************/
Vector3D vectorCrossProduct (Vector3D* aVectorA, Vector3D* aVectorB)
{
    Vector3D result;
    result.x = aVectorA->y*aVectorB->z - aVectorA->z*aVectorB->y;
    result.y = aVectorA->z*aVectorB->x - aVectorA->x*aVectorB->z;
    result.z = aVectorA->x*aVectorB->y - aVectorA->y*aVectorB->x;

    return result;
}

/****************************************************//**
 * Writes the value of a vector into logfile.
 ********************************************************/
void vectorWrite(Vector3D& aVector)
{
    Logger logger;
    logger.write("("); 
    logger.write(aVector.x); logger.write(";");
    logger.write(aVector.y); logger.write(";");
    logger.write(aVector.z); logger.write(")\n");
}


/****************************************************//**
 * This method sets some (nice) default values for
 * rendering with light. (e.g. glMaterialfv( ..., GL_AMBIENT, ...))
 ********************************************************/
void setDefaultGLMaterialValues()
{
    GLfloat ambient[4] = {0.5f, 0.5f, 0.5f, 1.0f};
    GLfloat diffuse[4] = {0.5f, 0.5f, 0.5f, 1.0f};
    GLfloat specular[4] = {0.2f, 0.2f, 0.2f, 1.0f};
    GLfloat emissive[4] = {0.0f, 0.0f, 0.0f, 0.0f};
    GLfloat shininess = 50.0f;

    glMaterialfv( GL_FRONT, GL_AMBIENT, ambient );
    glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
    glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
    glMaterialfv( GL_FRONT, GL_EMISSION, emissive );
    glMaterialf( GL_FRONT, GL_SHININESS, shininess );
}

/****************************************************//**
 * This method detects the Filename out of a given string.\n
 * "c:\windows\winhelp.exe" -> "winhelp.exe"\n
 * "/usr/bin/man" -> "man"
 ********************************************************/
std::string splitFileName(const std::string& aFile)
{
    std::size_t found = aFile.find_last_of("/\\");
    if (found != aFile.npos)
        found++;
    return aFile.substr(found);
}

/****************************************************//**
 * This method detects the Filenextension out of a given 
 * string.\n
 * "c:\windows\winhelp.exe" -> "exe"\n
 * "/usr/bin/man" -> ""
 ********************************************************/
std::string splitFileExtension(const std::string& aFile)
{
    std::size_t found = aFile.find_last_of(".");
    if (found != aFile.npos)
        found++;
    return aFile.substr(found);
}

/****************************************************//**
 * This method detects the Foldername out of a given string.\n
 * "c:\windows\winhelp.exe" -> "c:\windows"\n
 * "/usr/bin/man" -> "/usr/bin"
 ********************************************************/
std::string splitFolderName(const std::string& aFile)
{
    std::size_t found = aFile.find_last_of("/\\");
    if (found == aFile.npos)
        found = 0;
    return aFile.substr(0,found);
}

/****************************************************//**
 * This method checks the OpenGl-Error-Flag and writes an
 * error into the logfile. (Descriptions copied from 
 * OpenGl-SDK Man pages)
 ********************************************************/
void checkGLErrors(const char* aFunctionName)
{
    GLenum errorNum = glGetError();
    
    if(errorNum != GL_NO_ERROR)
    {
        Logger logger;
        logger.error("GL_ERROR", aFunctionName, "A critical OpenGl-error occurred.");
        logger.write("  error number: "); logger.write(errorNum);
        
        switch(errorNum)
        {
        case GL_INVALID_ENUM:
            logger.write(" (GL_INVALID_ENUM)\n"
                    "  An unacceptable value is specified for an enumerated argument.\n");
            break;
        case GL_INVALID_VALUE:
            logger.write(" (GL_INVALID_VALUE)\n"
                    "  A numeric argument is out of range.\n");
            break;
        case GL_INVALID_OPERATION:
            logger.write(" (GL_INVALID_OPERATION)\n"
                    "  The specified operation is not allowed in the current state.\n");
            break;
        case GL_STACK_OVERFLOW:
            logger.write(" (GL_STACK_OVERFLOW)\n"
                    "  This command would cause a stack overflow.\n");
            break;
        case GL_STACK_UNDERFLOW:
            logger.write(" (GL_STACK_UNDERFLOW)\n"
                    "  This command would cause a stack underflow.\n");
            break;
        case GL_OUT_OF_MEMORY:
            logger.write(" (GL_OUT_OF_MEMORY)\n"
                    "  There is not enough memory left to execute the command.\n");
            break;
        default:
            logger.write(" (unknown Error)\n");
        }//switch (errorNum)
        
        
    } //if (errorNum != GL_NO_ERROR)
}

} //namespace
